module.exports = class {
  constructor() {
    this.deleteChunkMap = new WeakMap();
  }

  recordDeleteChunk(entry, deleteEntryChunk) {
    const record = this.deleteChunkMap.get(entry)
    if (record) {
      record.add(deleteEntryChunk);
    } else {
      this.deleteChunkMap.set(entry, new Set([deleteEntryChunk]));
    }
  }

  // 是否是split产生的chunk
  isSplitChunk(chunk) {
    return /^(reused as )?split chunk/.test(chunk.chunkReason);
  }

  // 是否是css相关的模块（css模块与 mini-css-extract-plugin 产生的js模块）
  isAllCssRelationModule(modules) {
    return [...modules].every(_module => _module.type === 'css/mini-extract' || _module._source._value.startsWith('// extracted by mini-css-extract-plugin'));
  }

  apply(compiler) {
    const self = this;
    compiler.hooks.compilation.tap('MyTestPlugin', compilation => {
      // 在createHash之前移除多余的chunk和module
      compilation.hooks.optimizeChunksAdvanced.tap('MyTestPlugin', chunks => {
        chunks.forEach(chunk => {
          // 只针对入口模块处理
          if (chunk.hasEntryModule()) {
            const entries = [...chunk.groupsIterable];
            entries.forEach(entry => {
              const subChunks = entry.chunks;
              const deleteChunkModules = new Set();
              for (let index = subChunks.length - 1; index >= 0; --index) {
                const subChunk = subChunks[index];
                if (self.isSplitChunk(subChunk) && self.isAllCssRelationModule(subChunk.getModules())) {
                  // 删除chunk并保存记录
                  const deleteChunk = subChunks.splice(index, 1)[0];
                  self.recordDeleteChunk(entry, deleteChunk);
                  // 记录该chunk的module，需要将它们从其它chunk中删除
                  deleteChunk.getModules().forEach(chunkModule => {
                    deleteChunkModules.add(chunkModule);
                  })
                }
              }
              
              if (deleteChunkModules.size) {
                // 从其它chunk中删除无用module
                subChunks.forEach(item => {
                  const entryDependencies = item.entryModule && item.entryModule.dependencies;
                  if (entryDependencies && entryDependencies.length) {
                    item.entryModule.dependencies = entryDependencies.filter(dep => !(dep.module && deleteChunkModules.has(dep.module)))
                  }
                })
              }
            })
          }
        })
      });

      // 在chunk的files确定后，将之前删除的chunk恢复
      compilation.hooks.additionalChunkAssets.tap('MyTestPlugin', chunks => {
        chunks.forEach(chunk => {
          const entries = [...chunk.groupsIterable];
          entries.forEach(entry => {
            const hasDeleteChunk = self.deleteChunkMap.get(entry);
            if (hasDeleteChunk) {
              entry.chunks = [...new Set([...entry.chunks, ...hasDeleteChunk])];
            }
          })
        })
      });

      // 在生成manifest时删除无用的file
      compilation.chunkTemplate.hooks.renderManifest.tap({ name: 'MyTestPlugin', stage: 1 }, result => {
        for(let index = result.length -1; index >= 0; --index) {
          const { chunk, contentHashType } = result[index].pathOptions;
          if (contentHashType === 'javascript' && self.isSplitChunk(chunk) && self.isAllCssRelationModule(chunk.getModules())) {
            result.splice(index, 1);
          }
        }
      });
    });
  }
}